//Copyright 2010 Neurosciences Research Foundation, Incorporated
#include <stdlib.h>
#include <stdint.h>
#include <mpi.h>
#include <iostream>
#include <limits.h>
#include <vector>
#include <math.h>

#ifndef STRINGIZE
#define STRINGIZE(x) (#x)
#endif // !STRINGIZE

#ifndef MAKE_STRING
#define MAKE_STRING(x) STRINGIZE(x)
#endif // !MAKE_STRING

#ifndef COMM_H
#define COMM_H

//#define UPSAMPLE 2

#include "generator.h"
UnifRandGen my_generator = UnifRandGen();

enum dummydata_indexes {
	START = 0,
	STOP = 1
};

struct file_input {
	int external_group;
	int start_second;
	int stop_second;
	int first_file;
	int last_file;
	int files_read;
	int zero_pad;
	bool loop_flag;
	bool wm_phase_flag;
	bool rand_file_flag;
	int min_file;
	int max_file;
};

struct comm {
	
	struct colors {
		enum constants {
			send,
			receive,
			master,
			slave
		};
	};
	
	static int world_rank;
	
	static int network_rank;
	static MPI_Comm network_comm;
	
	static int exchange_rank;
	static MPI_Comm exchange_comm;
	
	// FOR DISPLAY:
	static int display_rank;
	static MPI_Comm display_comm;
	
	static int color;
	
	static void init(int color);
};


#endif // !COMM_H



int comm::world_rank;

int comm::network_rank;
MPI_Comm comm::network_comm;

int comm::exchange_rank;
MPI_Comm comm::exchange_comm;

int comm::color;

// FOR DISPLAY:
int comm::display_rank;
MPI_Comm comm::display_comm;

void comm::init(int color)
{
	// world rank
	MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
	
	// split
	MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &network_comm);
	MPI_Comm_rank(network_comm, &network_rank);
	
	// split roots
#define MASTER 2
#define SLAVE 3
	
	MPI_Comm_split(MPI_COMM_WORLD,
				   network_rank == 0 ? MASTER : SLAVE,
				   color == colors::send ? 0 : 1,
				   &exchange_comm);
	
	MPI_Comm_rank(exchange_comm, &exchange_rank);
	
#ifdef DISPLAY_WINDOW
	MPI_Comm_split(MPI_COMM_WORLD, 101, world_rank+1, &display_comm);
	MPI_Comm_rank(display_comm, &display_rank);
#endif
	
};


void read_input_spikes_from_file (int t_sec, int *spike_counts, int **spikes, int **bounds_matrix, int max_spikes_msec, std::vector<file_input*> file_stims)
{
	int temp_time, temp_index, current_file, stim, i, g;
	char spikes_filename[128];
	FILE *fspikes_in;
	
	for (i = 0; i < 1000; i++)
		spike_counts[i] = 0;
	
	for ( stim = 0; stim < file_stims.size(); stim++ )
	{
		if ( (t_sec >= file_stims[stim]->start_second) && (t_sec < file_stims[stim]->stop_second) )
		{
			current_file = file_stims[stim]->first_file + file_stims[stim]->files_read;
			file_stims[stim]->files_read++;
			
			if ( (current_file > file_stims[stim]->last_file) && (file_stims[stim]->loop_flag) )
			{
				if ( current_file > ( file_stims[stim]->last_file + file_stims[stim]->zero_pad ) )
				{
					// wm phase:
					if ( file_stims[stim]->wm_phase_flag )
					{
						if ( file_stims[stim]->rand_file_flag )
						{
							file_stims[stim]->rand_file_flag = false;
							file_stims[stim]->first_file++;
							if ( file_stims[stim]->first_file > file_stims[stim]->max_file )
								file_stims[stim]->first_file = file_stims[stim]->min_file;
							file_stims[stim]->last_file = file_stims[stim]->first_file;
						}
						else
						{
							//file_stims[stim]->rand_file_flag = true;
							file_stims[stim]->first_file++;
							if ( file_stims[stim]->first_file > file_stims[stim]->max_file )
								file_stims[stim]->first_file = file_stims[stim]->min_file;
							file_stims[stim]->last_file = file_stims[stim]->first_file;
						}
					}
					
					// reset:
					file_stims[stim]->files_read = 0;
					// and repeat:
					current_file = file_stims[stim]->first_file + file_stims[stim]->files_read;
					file_stims[stim]->files_read++;
				}
				else
					break;
			}
			
			if ( current_file > file_stims[stim]->last_file )
				break;
			
			std::cout << "DSIM " << t_sec*1000 << " " << current_file << " FR: " << file_stims[stim]->files_read << std::endl;
			
			// wm phase:
			if ( (file_stims[stim]->wm_phase_flag) && (file_stims[stim]->rand_file_flag) )
			{
				if ( my_generator.Urand01() > 0.5 )
					current_file = my_generator.UrandI( file_stims[stim]->min_file, file_stims[stim]->max_file );
			}
			
			sprintf (spikes_filename, "%s/dummydata/spikes%d000.dat", MAKE_STRING(SIMDIR), current_file);
			fspikes_in = fopen (spikes_filename,"r");
			std::cout << "DSIM " << t_sec*1000 << " " << current_file << " FR: " << file_stims[stim]->files_read << std::endl;
			
			if (fspikes_in == NULL)
				break;
			
			std::cerr << std::endl << std::endl << "Reading In: " << spikes_filename << ", sec = " << t_sec << std::endl;
			i = 0;
			while ( fscanf(fspikes_in, "%d %d", &temp_time, &temp_index) != EOF )
			{
				g = file_stims[stim]->external_group;
				if ( (temp_index >= bounds_matrix[g][START]) && (temp_index <= bounds_matrix[g][STOP]) )
				{
					if (temp_time%1000 > 900) {
						break;
					}
					if ( spike_counts[ temp_time%1000 ] == max_spikes_msec )
					{
						std::cerr << "Number of external spikes for a single millisecond EXCEEDS number of external neurons!" << std::endl;
					}
					else
					{
#ifdef UPSAMPLE
						int x = (temp_index - bounds_matrix[g][START]) % 21;
						int y = (temp_index - bounds_matrix[g][START]) / 21;
						int x_bar = x * UPSAMPLE;
						int y_bar = y * UPSAMPLE;
						int new_index = x_bar + y_bar * 40;
						spikes[ temp_time%1000 ][ spike_counts[ temp_time%1000 ] ] = new_index;
#else
						spikes[ temp_time%1000 ][ spike_counts[ temp_time%1000 ] ] = temp_index;
#endif
						spike_counts[ temp_time%1000 ]++;
					}
				}
			}
			fclose (fspikes_in);
		}
	}
};


int** get_external_group_size (int *num_neurons, int *num_groups)
{
	int **bounds_matrix;
	int total_size, xTo, yTo, xFrom, yFrom, start_i_from, stop_i_from, counter, i, ret;
	char area[8], type[8], external_filename[128];
	FILE *fin;
	
	sprintf (external_filename, "%s/groups_external.dat", MAKE_STRING(SIMDIR));
	fin = fopen (external_filename,"r");
	
	if (fin == NULL)
	{
		std::cerr << "No external groups file found!" << std::endl;
		*num_groups = 0;
		*num_neurons = 0;
		return NULL;
	}
	
	total_size = 0;
	counter = 0;
	while ( fscanf(fin, "%s", area) != EOF )
	{
		ret = fscanf(fin, "%s", type);
		ret = fscanf(fin, "%d %d %d %d", &xFrom, &yFrom, &start_i_from, &stop_i_from);
		
		total_size = total_size + (xFrom * yFrom);
		counter++;
	}
	rewind (fin);
	
	// MALLOC 2D MATRIX!!!
	bounds_matrix = (int **)malloc( sizeof(int *) * counter );
	if ( bounds_matrix == NULL )
	{
		fprintf(stderr,"Cannot allocate memory --- dummy_sym\n");
		exit(0);
	}
	for (i = 0; i < counter; i++) 
	{
		bounds_matrix[i] = (int *)malloc( sizeof(int) * 2 );
		if ( bounds_matrix[i] == NULL )
		{
			fprintf(stderr,"Cannot allocate memory --- dummy_sym\n");
			exit(0);
		}
	}
	
	counter = 0;
	while ( fscanf(fin, "%s", area) != EOF )
	{
		ret = fscanf(fin, "%s", type);
		ret = fscanf(fin, "%d %d %d %d", &xFrom, &yFrom, &start_i_from, &stop_i_from);
		bounds_matrix[counter][START] = start_i_from;
		bounds_matrix[counter][STOP] = stop_i_from;
		counter++;
	}
	*num_groups = counter;
	*num_neurons = total_size;
	
	fclose (fin);
	return bounds_matrix;
};


using namespace std;

int main(int argc, char* argv[])
{
	MPI_Init(&argc, &argv);
	
	comm::init(comm::colors::send);
	
	int numprocs;
	MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
	cout << numprocs << endl;
	
	int length = 0;
	int num_external_neurons;
	int num_external_groups;
	int *spike_count_buffer;
	int **spike_buffer;
	int **external_groups_bounds;
	
	// READ IN DUMMY GROUPS
	external_groups_bounds = get_external_group_size(&num_external_neurons, &num_external_groups);
	std::cerr << "*** Total External Groups " << num_external_groups << std::endl;
	for (int i = 0; i < num_external_groups; i++)
		std::cerr << "*** External Group " << i+1 << ", From: " << external_groups_bounds[i][START] << " - " << external_groups_bounds[i][STOP] << std::endl;
	
	spike_count_buffer = (int *)malloc( sizeof(int) * 1000 );
	spike_buffer = (int **)malloc( sizeof(int *) * 1000 );
	if ( (spike_count_buffer == NULL) | (spike_buffer == NULL) )
	{
		fprintf(stderr,"Cannot allocate memory --- dummy_sym\n");
		exit(0);
	}
	for (int i = 0; i < 1000; i++) {
		spike_buffer[i] = (int *)malloc( sizeof(int) * num_external_neurons );
		if ( spike_buffer[i] == NULL )
		{
			fprintf(stderr,"Cannot allocate memory --- dummy_sym\n");
			exit(0);
		}
	}
	
	// CREATE EXTERNAL SPIKE INPUTS
	std::vector <file_input*> external_inputs;
	external_inputs.resize(128+3);//(128+3);//(64+3);//(128+3);//(35);//(19); // MAKE SURE TO CHANGE THE SIZE WHEN ADDING FILE INPUTS
	{
		/*
		 HOW TO USE:
		 
		 external_inputs[i] = (file_input *)malloc( sizeof(file_input) );
		 external_inputs[i]->external_group = 0;
		 external_inputs[i]->start_second = 0;
		 external_inputs[i]->stop_second = 3600;
		 external_inputs[i]->first_file = 1;
		 external_inputs[i]->last_file = 59;
		 external_inputs[i]->files_read = 0; <-- ***MUST BE ZERO***
		 external_inputs[i]->zero_pad = 1;
		 external_inputs[i]->loop = true;
		 
		 This will read the spikes for the 1st group in 'groups_external.dat' and enter them into the
		 simulation for the first 1 hour of the simulation (0 - 3600 seconds). It will start with
		 file 'spikes1000.dat' and go to 'spikes59000.dat'. Since the loop variable is true, 
		 it will then add 1 second of no input (zeor_pad) and then loop to the beginning and use 'spikes1000.dat' 
		 again. This will continue until continue until simulation time reaches 3600 seconds. The files_read 
		 variable should always be set to 0 initially.
		 */
				
		//#define QUICK_TEST
		#ifdef QUICK_TEST
		external_inputs[0] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[0]->external_group = 0;
		external_inputs[0]->start_second = 0;
		external_inputs[0]->stop_second = 3;
		external_inputs[0]->first_file = 5;
		external_inputs[0]->last_file = 5;
		external_inputs[0]->files_read = 0;
		external_inputs[0]->zero_pad = 0;
		external_inputs[0]->loop_flag = true;
		external_inputs[0]->wm_phase_flag = false;
		external_inputs[0]->rand_file_flag = false;
		
		
		external_inputs[1] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[1]->external_group = 0;
		external_inputs[1]->start_second = 3;
		external_inputs[1]->stop_second = 6;
		external_inputs[1]->first_file = 6;
		external_inputs[1]->last_file = 6;
		external_inputs[1]->files_read = 0;
		external_inputs[1]->zero_pad = 0;
		external_inputs[1]->loop_flag = true;
		external_inputs[1]->wm_phase_flag = false;
		external_inputs[1]->rand_file_flag = false;
		
		
		
		external_inputs[2] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[2]->external_group = 0;
		external_inputs[2]->start_second = 6;
		external_inputs[2]->stop_second = 9;
		external_inputs[2]->first_file = 7;
		external_inputs[2]->last_file = 7;
		external_inputs[2]->files_read = 0;
		external_inputs[2]->zero_pad = 0;
		external_inputs[2]->loop_flag = true;
		external_inputs[2]->wm_phase_flag = false;
		external_inputs[2]->rand_file_flag = false;
		
		
		external_inputs[3] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[3]->external_group = 0;
		external_inputs[3]->start_second = 9;
		external_inputs[3]->stop_second = 12;
		external_inputs[3]->first_file = 8;
		external_inputs[3]->last_file = 8;
		external_inputs[3]->files_read = 0;
		external_inputs[3]->zero_pad = 0;
		external_inputs[3]->loop_flag = true;
		external_inputs[3]->wm_phase_flag = false;
		external_inputs[3]->rand_file_flag = false;
		
		external_inputs[4] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[4]->external_group = 0;
		external_inputs[4]->start_second = 12;
		external_inputs[4]->stop_second = 15;
		external_inputs[4]->first_file = 9;
		external_inputs[4]->last_file = 9;
		external_inputs[4]->files_read = 0;
		external_inputs[4]->zero_pad = 0;
		external_inputs[4]->loop_flag = true;
		external_inputs[4]->wm_phase_flag = false;
		external_inputs[4]->rand_file_flag = false;
		
				external_inputs[5] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[5]->external_group = 0;
		external_inputs[5]->start_second = 15;
		external_inputs[5]->stop_second = 18;
		external_inputs[5]->first_file = 10;
		external_inputs[5]->last_file = 10;
		external_inputs[5]->files_read = 0;
		external_inputs[5]->zero_pad = 0;
		external_inputs[5]->loop_flag = true;
		external_inputs[5]->wm_phase_flag = false;
		external_inputs[5]->rand_file_flag = false;
		
		
				external_inputs[6] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[6]->external_group = 0;
		external_inputs[6]->start_second = 18;
		external_inputs[6]->stop_second = 21;
		external_inputs[6]->first_file = 11;
		external_inputs[6]->last_file = 11;
		external_inputs[6]->files_read = 0;
		external_inputs[6]->zero_pad = 0;
		external_inputs[6]->loop_flag = true;
		external_inputs[6]->wm_phase_flag = false;
		external_inputs[6]->rand_file_flag = false;
		
		
				external_inputs[7] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[7]->external_group = 0;
		external_inputs[7]->start_second = 21;
		external_inputs[7]->stop_second = 24;
		external_inputs[7]->first_file = 12;
		external_inputs[7]->last_file = 12;
		external_inputs[7]->files_read = 0;
		external_inputs[7]->zero_pad = 0;
		external_inputs[7]->loop_flag = true;
		external_inputs[7]->wm_phase_flag = false;
		external_inputs[7]->rand_file_flag = false; 
		#endif	
		
#define ORIGINAL_STIM
#ifdef ORIGINAL_STIM
		//pattern training: Mirror-J/J, 5 through 12
		external_inputs[0] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[0]->external_group = 0;
		external_inputs[0]->start_second = 0;
		external_inputs[0]->stop_second = 168;
		external_inputs[0]->first_file = 9;
		external_inputs[0]->last_file = 16;
		external_inputs[0]->files_read = 0;
		external_inputs[0]->zero_pad = 0;
		external_inputs[0]->loop_flag = true;
		external_inputs[0]->wm_phase_flag = false;
		external_inputs[0]->rand_file_flag = false;
		
		//sequence training: Mirror-J, 5 through 8  
		external_inputs[1] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[1]->external_group = 0;
		external_inputs[1]->start_second = 168;
		external_inputs[1]->stop_second = 214;
		external_inputs[1]->first_file = 9;
		external_inputs[1]->last_file = 12;
		external_inputs[1]->files_read = 0;
		external_inputs[1]->zero_pad = 0;
		external_inputs[1]->loop_flag = true;		
		external_inputs[1]->wm_phase_flag = false;
		external_inputs[1]->rand_file_flag = false;
		
		//sequence training: J, 9 through 12
		external_inputs[2] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[2]->external_group = 0;
		external_inputs[2]->start_second = 216;
		external_inputs[2]->stop_second = 262;
		external_inputs[2]->first_file = 13;
		external_inputs[2]->last_file = 16;
		external_inputs[2]->files_read = 0;
		external_inputs[2]->zero_pad = 0;
		external_inputs[2]->loop_flag = true;
		external_inputs[2]->wm_phase_flag = false;
		external_inputs[2]->rand_file_flag = false;
#endif
		
		//#define WM_TESTBENCH_STIMULUS
#ifdef WM_TESTBENCH_STIMULUS
		
		int file_start_time = 231+0;//311;//231;
		//V1 TCs
		external_inputs[0] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[0]->external_group = 0;
		external_inputs[0]->start_second = 0;
		external_inputs[0]->stop_second = 320;
		external_inputs[0]->first_file = file_start_time;
		external_inputs[0]->last_file = 550;
		external_inputs[0]->files_read = 0;
		external_inputs[0]->zero_pad = 0;
		external_inputs[0]->loop_flag = false;
		external_inputs[0]->wm_phase_flag = false;
		external_inputs[0]->rand_file_flag = false;
		
		//V1 p23
		external_inputs[1] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[1]->external_group = 1;
		external_inputs[1]->start_second = 0;
		external_inputs[1]->stop_second = 320;
		external_inputs[1]->first_file = file_start_time;
		external_inputs[1]->last_file = 550;
		external_inputs[1]->files_read = 0;
		external_inputs[1]->zero_pad = 0;
		external_inputs[1]->loop_flag = false;
		external_inputs[1]->wm_phase_flag = false;
		external_inputs[1]->rand_file_flag = false;
		
		//V1 p6(L4)
		external_inputs[2] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[2]->external_group = 2;
		external_inputs[2]->start_second = 0;
		external_inputs[2]->stop_second = 320;
		external_inputs[2]->first_file = file_start_time;
		external_inputs[2]->last_file = 550;
		external_inputs[2]->files_read = 0;
		external_inputs[2]->zero_pad = 0;
		external_inputs[2]->loop_flag = false;
		external_inputs[2]->wm_phase_flag = false;
		external_inputs[2]->rand_file_flag = false;
		
		//P p23
		external_inputs[3] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[3]->external_group = 3;
		external_inputs[3]->start_second = 0;
		external_inputs[3]->stop_second = 320;
		external_inputs[3]->first_file = file_start_time;
		external_inputs[3]->last_file = 550;
		external_inputs[3]->files_read = 0;
		external_inputs[3]->zero_pad = 0;
		external_inputs[3]->loop_flag = false;
		external_inputs[3]->wm_phase_flag = false;
		external_inputs[3]->rand_file_flag = false;
		
		//P p6(L4)
		external_inputs[4] = (file_input *)malloc( sizeof(file_input) );
		external_inputs[4]->external_group = 4;
		external_inputs[4]->start_second = 0;
		external_inputs[4]->stop_second = 320;
		external_inputs[4]->first_file = file_start_time;
		external_inputs[4]->last_file = 550;
		external_inputs[4]->files_read = 0;
		external_inputs[4]->zero_pad = 0;
		external_inputs[4]->loop_flag = false;
		external_inputs[4]->wm_phase_flag = false;
		external_inputs[4]->rand_file_flag = false;
		
		
#endif
		
#define FULL_ROTATION_TEST
#ifdef FULL_ROTATION_TEST
		int last_mem = 3;
		int pattern_A = 9;//5;
		int pattern_B = 9;//5;
		int min_pattern_A = 9;//5;
		int max_pattern_A = 16;//12;
		int min_pattern_B = 9;//5;
		int max_pattern_B = 16;//12;
		int start_second = 270;
		int pattern_A_sec = 0;
		int pattern_B_sec = 2;
		int zero_pad_A = 1;
		int zero_pad_B = 2;
		int trial_duration_sec = 5;
		int current_time = start_second;
		int number_patterns = 8;
		int number_trials = number_patterns*number_patterns;
		int setups_per_trial = 2;
		int num_setups = setups_per_trial*number_trials;//16, 32, 128,
		for( int i = 0; i < num_setups ; ++i )
		{
			//Fixed Parameters
			external_inputs[i + last_mem] = (file_input *)malloc( sizeof(file_input) );
			external_inputs[i + last_mem]->external_group = 0;
			external_inputs[i + last_mem]->files_read = 0;
			external_inputs[i + last_mem]->loop_flag = true;
			external_inputs[i + last_mem]->wm_phase_flag = false;
			external_inputs[i + last_mem]->rand_file_flag = false; 
			
			if( (current_time%trial_duration_sec) == pattern_A_sec )
			{
				if( current_time != start_second )
				{
					if((current_time-start_second)%(number_patterns*trial_duration_sec) == 0)
					{
						++pattern_A;
						if( pattern_A > max_pattern_A ){
							pattern_A = min_pattern_A;
						}
					}
				}
				
				//present pattern A on every 5th second or end of a trial
				external_inputs[i + last_mem]->first_file = pattern_A;
				external_inputs[i + last_mem]->last_file = pattern_A;
				external_inputs[i + last_mem]->zero_pad = zero_pad_A;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_A + 1;
				current_time += zero_pad_A + 1;
			}
			else if( (current_time%trial_duration_sec) == pattern_B_sec )
			{
				//present pattern B on every 6th second and then turn off the pattern 
				//for 3 seconds, so the network has time to match the pattern
				external_inputs[i + last_mem]->first_file = pattern_B;
				external_inputs[i + last_mem]->last_file = pattern_B;
				external_inputs[i + last_mem]->zero_pad = zero_pad_B;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_B + 1;
				
				++pattern_B;
				if( pattern_B > max_pattern_B ){
					pattern_B = min_pattern_B;
				}
				current_time += zero_pad_B + 1;
			}
		}
#endif

//#define MATCH_FIVE_TO_EIGHT_ROTATION_TEST
#ifdef MATCH_FIVE_TO_EIGHT_ROTATION_TEST
		int last_mem = 3;
		int pattern_A = 5;
		int pattern_B = 5;
		int min_pattern_A = 5;
		int max_pattern_A = 8;
		int min_pattern_B = 5;
		int max_pattern_B = 8;
		int start_second = 270;
		int pattern_A_sec = 0;
		int pattern_B_sec = 2;
		int zero_pad_A = 1;
		int zero_pad_B = 2;
		int trial_duration_sec = 5;
		int current_time = start_second;
		int number_patterns = 4;
		int number_trials = number_patterns*number_patterns;
		int setups_per_trial = 2;
		int num_setups = setups_per_trial*number_trials;//16, 32, 128,
		for( int i = 0; i < num_setups ; ++i )
		{
			//Fixed Parameters
			external_inputs[i + last_mem] = (file_input *)malloc( sizeof(file_input) );
			external_inputs[i + last_mem]->external_group = 0;
			external_inputs[i + last_mem]->files_read = 0;
			external_inputs[i + last_mem]->loop_flag = true;
			external_inputs[i + last_mem]->wm_phase_flag = false;
			external_inputs[i + last_mem]->rand_file_flag = false; 
			
			if( (current_time%trial_duration_sec) == pattern_A_sec)
			{
				if( current_time != start_second )
				{
					if((current_time-start_second)%(number_patterns*trial_duration_sec) == 0)
					{
						++pattern_A;
						if( pattern_A > max_pattern_A ){
							pattern_A = min_pattern_A;
						}
					}
				}
				
				//present pattern A on every 5th second or end of a trial
				external_inputs[i + last_mem]->first_file = pattern_A;
				external_inputs[i + last_mem]->last_file = pattern_A;
				external_inputs[i + last_mem]->zero_pad = zero_pad_A;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_A + 1;
				
				current_time += zero_pad_A + 1;
			}
			else if( (current_time%trial_duration_sec) == pattern_B_sec )
			{
				//present pattern B on every 6th second and then turn off the pattern 
				//for 3 seconds, so the network has time to match the pattern
				external_inputs[i + last_mem]->first_file = pattern_B;
				external_inputs[i + last_mem]->last_file = pattern_B;
				external_inputs[i + last_mem]->zero_pad = zero_pad_B;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_B + 1;
				
				++pattern_B;
				if( pattern_B > max_pattern_B ){
					pattern_B = min_pattern_B;
				}
				current_time += zero_pad_B + 1;
			}
		}
#endif

//#define MATCH_NINE_TO_TWELVE_ROTATION_TEST
#ifdef MATCH_NINE_TO_TWELVE_ROTATION_TEST
		last_mem = 3 + 32;
		pattern_A = 9;
		pattern_B = 9;
		min_pattern_A = 9;
		max_pattern_A = 12;
		min_pattern_B = 9;
		max_pattern_B = 12;
		start_second = 270 + trial_duration_sec*16;
		pattern_A_sec = 0;
		pattern_B_sec = 2;
		zero_pad_A = 1;
		zero_pad_B = 2;
		trial_duration_sec = 5;
		current_time = start_second;
		number_patterns = 4;
		number_trials = number_patterns*number_patterns;
		setups_per_trial = 2;
		num_setups = setups_per_trial*number_trials;//16, 32, 128,
		for( int i = 0; i < num_setups ; ++i )
		{
			//Fixed Parameters
			external_inputs[i + last_mem] = (file_input *)malloc( sizeof(file_input) );
			external_inputs[i + last_mem]->external_group = 0;
			external_inputs[i + last_mem]->files_read = 0;
			external_inputs[i + last_mem]->loop_flag = true;
			external_inputs[i + last_mem]->wm_phase_flag = false;
			external_inputs[i + last_mem]->rand_file_flag = false; 
			
			if( (current_time%trial_duration_sec) == pattern_A_sec)
			{
				if( current_time != start_second )
				{
					if((current_time-start_second)%(number_patterns*trial_duration_sec) == 0)
					{
						++pattern_A;
						if( pattern_A > max_pattern_A ){
							pattern_A = min_pattern_A;
						}
					}
				}
				
				//present pattern A on every 5th second or end of a trial
				external_inputs[i + last_mem]->first_file = pattern_A;
				external_inputs[i + last_mem]->last_file = pattern_A;
				external_inputs[i + last_mem]->zero_pad = zero_pad_A;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_A + 1;
				
				current_time += zero_pad_A + 1;
			}
			else if( (current_time%trial_duration_sec) == pattern_B_sec )
			{
				//present pattern B on every 6th second and then turn off the pattern 
				//for 3 seconds, so the network has time to match the pattern
				external_inputs[i + last_mem]->first_file = pattern_B;
				external_inputs[i + last_mem]->last_file = pattern_B;
				external_inputs[i + last_mem]->zero_pad = zero_pad_B;
				external_inputs[i + last_mem]->start_second = current_time;
				external_inputs[i + last_mem]->stop_second = current_time + zero_pad_B + 1;
				
				++pattern_B;
				if( pattern_B > max_pattern_B ){
					pattern_B = min_pattern_B;
				}
				current_time += zero_pad_B + 1;
			}
		}
#endif
		
	}
	
	
	if (argc >= 2)
		length = atoi(argv[1]);
	int* array = new int[length];	
	//j => 230000 ms to start a checkpoint
	int checkpoint_ms = 0;//270000;// 230000;
	for(int j = checkpoint_ms; j < INT_MAX;j++)
	{
		if (comm::network_rank == 0)
		{
			if ( (num_external_neurons == 0) || (external_inputs.size() == 0) )
			{
				for (int32_t i = 0, v = j/20; i < length; ++i, v = v+10)
					array[i] = v;
				
				//cerr << "Bcast1" << endl;	
				MPI_Bcast(&length, 1, MPI_INT, 0, comm::exchange_comm);
				//cerr << "Bcast2" << endl;	
				MPI_Bcast(array, length, MPI_INT, 0, comm::exchange_comm);
				//DEBUG: cerr << "**** SENDER before 'barrier' ****" << array[9] <<endl;
				MPI_Barrier(comm::exchange_comm); // Without this, the external simulator may get ahead creating buffer overflow.
												  //DEBUG: cerr << "**** SENDER after barrier ****" << array[9] <<endl;
			}
			else
			{
				if ( j%1000 == 0 )
				{
					//cerr << "about to read dummy spikes " << endl << flush;
					
					// READ DUMMYDATA FILE FOR THIS SECOND
					read_input_spikes_from_file ( floor( j/1000 ), spike_count_buffer, spike_buffer, external_groups_bounds, num_external_neurons, external_inputs);
				}
				MPI_Bcast( &spike_count_buffer[ j%1000 ], 1, MPI_INT, 0, comm::exchange_comm);
				MPI_Bcast( spike_buffer[ j%1000 ], spike_count_buffer[ j%1000 ], MPI_INT, 0, comm::exchange_comm);
				MPI_Barrier( comm::exchange_comm );
			}
		}
	}
	cerr << "******** sender is quitting" << endl;
	
	// FREE ALLOCATED MEMORY
	for (int i = 0; i < 1000; i++)
		free ( spike_buffer[i] );
	free ( spike_buffer );
	free ( spike_count_buffer );
	if ( external_groups_bounds != NULL )
	{
		for (int i = 0; i < num_external_groups; i++)
			free ( external_groups_bounds[i] );
		free ( external_groups_bounds );
	}
	
	// FINISH
	MPI_Finalize();
	return 0;
}
